﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Multithreading多线程
{
    /// <summary>
    /// Task 多线程 3.0 在ThreadPool线程池又添加一些API
    /// 
    /// 
    /// </summary>
    public class MyTaskDoem
    {
        /// <summary>
        /// Task 的使用
        /// Task 的创建还是差不多的
        /// </summary>
        public static void Show()
        {
            //实例方式
            Task task = new Task(() =>
            {
                Console.WriteLine("无返回参数的委托");
            });

            //无参有返回值
            Task<string> task1 = new Task<string>(() =>
            {
                return "我是返回值";
            });

            //有参有返回值
            Task<string> task2 = new Task<string>(x =>
            {
                return "返回值 -- " + x.ToString();
            }, "我是输入参数");
            //开启线程
            task2.Start();
            //获取返回值 Result会堵塞线程获取返回值
            Console.WriteLine(task2.Result);

            //使用线程工厂创建 无参数无返回值线程
            Task.Factory.StartNew(() =>
            {
                Console.WriteLine("这个是线程工厂创建");
            }).Start();

            //使用线程工厂创建 有参数有返回值线程
            Task.Factory.StartNew(x =>
            {
                return "返回值 -- " + x.ToString(); ;
            }, "我是参数");

            //直接静态方法运行
            Task.Run(() =>
            {
                Console.WriteLine("无返回参数的委托");
            });
        }

        /// <summary>
        /// Task 的进阶
        /// 在这里我们可以看到当我们设置了线程池的线程
        /// 之后我们启动的线程就变少了。
        /// </summary>
        public static void Show1()
        {
            //我们设置线程池的处理线程和I/O线程
            ThreadPool.SetMaxThreads(4, 4);
            for (int i = 0; i < 10; i++)
            {
                //在这里之所以创建一个k、后面统一的讲哈哈
                int k = i;
                Task.Run(() =>
                {
                    Running(k);
                });
            }
            Console.ReadLine();
        }

        /// <summary>
        /// Task 的进阶
        /// Wait 等待Task线程完成才会执行后续动作
        /// WaitAll 等待Task[] 线程数组全部执行成功之后才会执行后续动作
        /// WaitAny 等待Task[] 线程数组任一执行成功之后就会执行后续动作
        /// WhenAll 等待Task[] 线程数组全部执行成功之后才会执行后续动作、与WaitAll不同的是他有回调函数ContinueWith
        /// WhenAny 等待Task[] 线程数组任一执行成功之后就会执行后续动作、与WaitAny不同的是他有回调函数ContinueWith
        /// ContinueWith 线程回调
        /// </summary>
        public static void Show2()
        {
            //创建一个线程使用Wait堵塞线程
            Task.Run(() =>
            {
                Console.WriteLine("Wait 等待Task线程完成才会执行后续动作");
            }).Wait();

            //创建一个装载线程的容器
            List<Task> list = new List<Task>();
            for (int i = 0; i < 10; i++)
            {
                list.Add(Task.Run(() =>
                {
                    Console.WriteLine("WaitAll 执行");
                }));
            }
            Task.WaitAll(list.ToArray());
            Console.WriteLine("Wait执行完毕");
            //清空容器
            list.Clear();
            for (int i = 0; i < 10; i++)
            {
                list.Add(Task.Run(() =>
                {
                    Console.WriteLine("WaitAny 执行");
                }));
            }
            Task.WaitAny(list.ToArray());
            Console.WriteLine("WaitAny 执行完毕");
            //清空容器
            list.Clear();
            for (int i = 0; i < 10; i++)
            {
                list.Add(Task.Run(() =>
                {
                    Console.WriteLine("WhenAll 执行");
                }));
            }
            Task.WhenAll(list.ToArray()).ContinueWith(x =>
            {
                return x.AsyncState;
            });
            Console.WriteLine("WhenAll 执行完毕");
            //清空容器
            list.Clear();
            for (int i = 0; i < 10; i++)
            {
                list.Add(Task.Run(() =>
                {
                    Console.WriteLine("WhenAny 执行");
                }));
            }
            Task.WhenAny(list.ToArray()).ContinueWith(x =>
            {
                return x.AsyncState;
            });
            Console.WriteLine("WhenAny 执行完毕");
            //清空容器
            list.Clear();
            Console.ReadLine();
        }

        /// <summary>
        /// Task 的进阶
        /// 我们执行100个任务想要限制线程的数量只有10个执行
        /// 这样我们可以达到限制执行的线程数量
        /// </summary>
        public static void Show3()
        {
            //创建一个装载线程的容器
            List<Task> list = new List<Task>();
            //执行100才任务
            for (int i = 0; i < 100; i++)
            {
                //在这里之所以创建一个k、后面统一的讲哈哈
                int k = i;
                //定义线程添加到容器中
                list.Add(Task.Run(() =>
                {
                    Running(k);
                }));
                //当容器的数量大于10的时候
                if (list.Count > 10)
                {
                    //执行容器中的线程，WaitAny如果有一个线程执行功能了之后。
                    Task.WaitAny(list.ToArray());
                    //将执行成功的线程释放，这里虽然我们将没有执行完的线程重新放入线程中执行了，但是他不会重复执行多次。
                    //原因：因为线程存放在堆里面的只会常见一个
                    list = list.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
                }
            }
            //WaitAll 要全部执行完之后才能释放堵塞
            //防止最后没有10个线程了全部执行
            Task.WaitAll(list.ToArray());
            Console.ReadLine();
        }

        /// <summary>
        /// TaskFactory 线程工厂
        /// </summary>
        public static void Show4()
        {
            //我们创建一个线程工厂
            TaskFactory taskFactory = new TaskFactory();
            //使用StartNew创建一个线程 无参无返回值,并且启动
            taskFactory.StartNew(() =>
            {
                Console.WriteLine("我是新的线程");
            });

            //使用StartNew创建一个线程 有参无返回值,并且启动
            taskFactory.StartNew(x =>
            {
                Console.WriteLine(x);
            }, "我是参数");

            //使用StartNew创建一个线程 有有返回值,并且启动
            taskFactory.StartNew(x =>
            {
                Console.WriteLine(x);
                return $"我是参数：{x} 我是返回值：王武 ";
            }, "我是参数");

            List<Task> tasks = new List<Task>();
            Action action = () =>
            {
                Console.WriteLine("我是一个快乐的线程");
            };
            for (int i = 0; i < 5; i++)
            {
                tasks.Add(Task.Run(action));
            }

            //使用ContinueWhenAll等待线程回调
            taskFactory.ContinueWhenAll(tasks.ToArray(), x =>
            {
                Console.WriteLine("我是回调函数哈哈");
            });

            Console.ReadLine();
        }

        /// <summary>
        /// Parallel 并发控制 是在Task的基础上做了封装 4.5
        /// 在上面我们执行100个任务，只能用到10个线程我们使用一些小技巧
        /// 这个方法就给我们提供了一些，更加便捷的方法。
        /// </summary>
        public static void Show5()
        {
            //第一种方法是
            Parallel.Invoke(() =>
            {
                Console.WriteLine("我是线程一号");
            }, () =>
            {
                Console.WriteLine("我是线程二号");
            }, () =>
            {
                Console.WriteLine("我是线程三号");
            });

            //for 方式创建多线程
            Parallel.For(0, 5, x =>
            {
                Console.WriteLine("这个看名字就知道是for了哈哈 i=" + x);
            });

            //ForEach 方式创建多线程
            Parallel.ForEach(new string[] { "0", "1", "2", "3", "4" }, x => Console.WriteLine("这个看名字就知道是ForEach了哈哈 i=" + x));

            //这个我们包一层，就不会卡主界面了
            Task.Run(() =>
            {
                //创建线程选项
                ParallelOptions parallelOptions = new ParallelOptions()
                {
                    MaxDegreeOfParallelism = 3
                };
                //创建一个并发线程
                Parallel.For(0, 5, parallelOptions, x =>
                {
                    Console.WriteLine("限制执行的次数");
                });
            }).Wait();
            Console.WriteLine("**************************************");

            //Break  Stop  都不推荐用
            ParallelOptions parallelOptions = new ParallelOptions();
            parallelOptions.MaxDegreeOfParallelism = 3;
            Parallel.For(0, 40, parallelOptions, (i, state) =>
            {
                if (i == 20)
                {
                    Console.WriteLine("线程Break，Parallel结束");
                    state.Break();//结束Parallel
                                  //return;//必须带上
                }
                if (i == 2)
                {
                    Console.WriteLine("线程Stop，当前任务结束");
                    state.Stop();//当前这次结束
                                 //return;//必须带上
                }
                Console.WriteLine("我是线程i=" + i);
            });
        }

        /// <summary>
        /// 一个执行需要长时间的任务
        /// </summary>
        static void Running(int s)
        {
            Console.WriteLine("**********************************");
            Console.WriteLine("执行开始啦" + s);
            Console.WriteLine("获取当前执行的线程ID：" + Thread.CurrentThread.ManagedThreadId.ToString());
            var j = 0;
            for (int i = 0; i < 1000000000; i++)
            {
                j++;
            }
            Console.WriteLine("执行结束啦" + s);
        }

        /// <summary>
        /// 一个执行需要长时间的任务
        /// </summary>
        static int Running()
        {
            Console.WriteLine("**********************************");
            Console.WriteLine("执行开始啦");
            Console.WriteLine("获取当前执行的线程ID：" + Thread.CurrentThread.ManagedThreadId.ToString());
            var j = 0;
            for (int i = 0; i < 1000000000; i++)
            {
                j++;
            }
            Console.WriteLine("执行结束啦");
            return j;
        }
    }
}
